在React Native中函数式的控制Loading Component

阅读前你需要知道的#

  • React Context是什么
  • 如何自定义Hooks

为什么需要函数式的控制Loading Component#

在APP的开发中,我们经常会遇到一些异步的操作,比如通过网络请求获取数据。在用户进行这类操作的时候,一般需要弹出一些提示的组件,用来表示这次网络请求所处的状态。 最简单的实现方法是维护一个内部State,在网络请求的状态发生变化的时候,按照业务需求同步更新这个内部状态,并且根据这个状态来决定Loading Component是否渲染,代码示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const TestLoading = () => {
const [isError, setIsError] = useState(false)
const handleRequest = async () => {
try {
await requestAPI()
setIsError(false)
}catch(e){
setIsError(ture)
}
}
return (
<>
<Button title='request' onPress={handleRequest} />
<Modal visible={isError}>
<ActivityIndicator size="large"/>
</Modal>
</>
)
}

从上面的代码我们不难看出,如果使用和这种简单的实现方式,需要在每个需要展示Loading Component的页面,手动添加它,并且还需要增加一个内部状态来控制它的展示。这不免显得有些冗余,如果过我们可以使用如下的函数式的调用方式来控制Loading Component,将大量减少冗余代码

1
2
3
4
5
6
7
8
9
10
11
12
const TestLoading = () => {
//...
const handleRequest = async () => {
showLoading()
await requestAPI()
closeLoading()
}
//...
return (
<Button title='request' onPress={handleRequest} />
)
}

要解决的问题#

  1. 和web不同的是,在Mobile的开发中,我们无法直接调用类似UIView这类的原生方法来增加新的Element
  2. 保持最大的兼容性,尽量不依赖Mobile平台提供的特殊API

实现#

设计思路#

对于这样的Loading Component在整个APP中可以只存在一个这样的组件,并将它在APP的最外层进行挂载,然后在通过React Context共享它的控制渲染的函数。这样,我们就可以在任意一个组件或者Hook中控制Loading Component了。

代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// GlobalLoaidngProvider.tsx
export const GlobalLoadingContext = React.createContext({
show: console.log
close: console.log
});

export const GlobalLoadingProvider: React.FC<React.PropsWithChildren> = ({
children
}) => {
const [isShow, setIsShow] = useState(false);
const [message, setMessage] = useState('');
const handleShow = (message?: string) => {
setIsShow(true);
message && setMessage(message);
}
const handleClose = () => {
setIsShow(false)
}
return (
<GlobalLoadingContext.Provider value={{
show: handleShow,
}}>
{children}
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
>

<ActivityIndicator size='large' />
<Text>{message}</Text>
</Modal>
</GlobalLoadingContext.Provider>
)
}

// App.tsx
// ...
const App = () => {
//...
return (
<GlobalLoadingProvider>
{//... you component}
</GlobalLoadingProvider>
)
}

//RequestAPIButton.tsx

const RequestAPI = () => {
const {show, close} = useContext(GlobalLoadingContext)
const handleRequest = async () => {
show("data lading");
await requestAPI();
close();
};

return(
<Button onPress={handleRequest} title="requestAPI" />
)
}
Author

Ashes Born

Posted on

2024-09-06

Updated on

2024-09-06

Licensed under

Kommentare